﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace XMLDocParser
{
    public class XMLDocParser
    {
        #region File Structure
        public class Assembly
        {
            public String Name;
            public List<Class> Classes;

            public Assembly()
            {
                Classes = new List<Class>();
            }
        }

        public class Class
        {
            public String Name;
            public String FullName;
            public String DeclaringType;
            public String Description;
            public List<Method> Methods;
            public List<Property> Properties;

            public Class()
            {
                Methods = new List<Method>();
                Properties = new List<Property>();
            }
        }

        public class Method
        {
            public String FullName;
            public String Name;
            public String DeclaringType;
            public String Description;
            public List<Parameter> Params;
            public String ReturnDescription;

            public Method()
            {
                Params = new List<Parameter>();
            }
        }

        public class Parameter
        {
            public Type Type;
            public String Name;
            public String Description;
        }

        public class Property
        {
            public String FullName;
            public String Name;
            public String DeclaringType;
            public String Description;
        }

        #endregion

        private Assembly parsedAssembly;
        public Assembly ParsedAssembly
        {
            get
            {
                return parsedAssembly;
            }
            private set
            {
                parsedAssembly = value;
            }
        }

        public XMLDocParser(String docFile)
        {
            XmlDocument document = new XmlDocument();
            document.Load(docFile);

            XmlNode rootNode = document.SelectSingleNode("doc");
            XmlNode assemblyNode = rootNode.SelectSingleNode("assembly");
            XmlNode assemblyNameNode = assemblyNode.SelectSingleNode("name");


            ParsedAssembly = new Assembly();
            ParsedAssembly.Name = assemblyNameNode.InnerText;
            ParsedAssembly.Classes = new List<Class>();
            XmlNodeList assemblyMembers = rootNode.SelectSingleNode("members").ChildNodes;
            foreach (XmlNode memberNode in assemblyMembers)
            {
                //Enthält den Namen
                String nameAttribute = memberNode.Attributes["name"].InnerText;
                //erster parameter ist der Typ: T für Type, M für Method, P für Property...
                String type = nameAttribute.Split(':')[0];
                //zweiter parameter ist der Pfad zum Typ, z.B.: Assemblyname + Klassenname + Methodenname...
                String typePath = nameAttribute.Split(':')[1];

                switch (type)
                {
                    case "T":
                        //Klasse, Enum, oder ähnliches:
                        Class newClass = new Class();
                        newClass.FullName = typePath;
                        String[] splittedTypePath = typePath.Split('.');
                        newClass.Name = splittedTypePath.Last();
                        for (int i = 0; i < splittedTypePath.Length - 1; i++)
                        {
                            newClass.DeclaringType += splittedTypePath[i] + ".";
                        }
                        newClass.DeclaringType = newClass.DeclaringType.Remove(newClass.DeclaringType.Length - 1);
                        newClass.Description = memberNode.SelectSingleNode("summary").InnerText;
                        ParsedAssembly.Classes.Add(newClass);
                        break;
                    case "M":
                        //Methode:
                        Method newMethod = new Method();
                        newMethod.Description = memberNode.SelectSingleNode("summary").InnerText;
                        
                        //completeType beinhaltet der Pfad ohne Parameterliste
                        String[] completeType = typePath.Split('(');
                        completeType = completeType[0].Split('.'); //Typen aufsplitten
                        String classType = "";

                        for (int i = 0; i < completeType.Length - 1; i++) //Den letzten Typen weglassen, um den Klassenpfad auszulesen
                        {
                            classType += completeType[i] + ".";
                        }
                        classType = classType.Remove(classType.Length - 1);

                        XmlNodeList paramNodes = memberNode.SelectNodes("param");

                        List<Parameter> parameterList = new List<Parameter>();
                        newMethod.FullName = typePath.Split('(')[0];
                        newMethod.Name = newMethod.FullName.Split('.').Last();
                        newMethod.DeclaringType = classType;
                        //Parametertypen auslesen und casten:

                        if (paramNodes != null && paramNodes.Count > 0)
                        {
                            String parameterTypes = typePath.Split('(')[1];
                            parameterTypes = parameterTypes.Remove(parameterTypes.Length - 1);
                            String[] parameterTypeList = parameterTypes.Split(',');
                            int j = 0;
                            foreach (XmlNode paramNode in paramNodes)
                            {
                                Parameter newParameter = new Parameter();
                                newParameter.Name = paramNode.Attributes["name"].InnerText;
                                newParameter.Description = paramNode.InnerText;
                                newParameter.Type = Type.GetType(parameterTypeList[j++]);

                                parameterList.Add(newParameter);
                            }
                            newMethod.Params = parameterList;
                        }
                        XmlNode returnNode = memberNode.SelectSingleNode("returns");
                        newMethod.ReturnDescription = returnNode == null ? null : returnNode.InnerText;

                        ParsedAssembly.Classes.Where(c => c.FullName == classType).First().Methods.Add(newMethod);

                        break;
                    case "P":
                        //Property
                        Property newProperty = new Property();
                        newProperty.FullName = typePath;
                        newProperty.Name = typePath.Split('.').Last();
                        String[] splittedPropertyTypePath = typePath.Split('.');
                        newProperty.Name = splittedPropertyTypePath.Last();
                        for (int i = 0; i < splittedPropertyTypePath.Length - 1; i++)
                        {
                            newProperty.DeclaringType += splittedPropertyTypePath[i] + ".";
                        }
                        newProperty.DeclaringType = newProperty.DeclaringType.Remove(newProperty.DeclaringType.Length - 1);
                        newProperty.Description = memberNode.SelectSingleNode("summary").InnerText;
                        break;
                    default:
                        continue;
                }

            }
        }


    }
}
